package cn.s1995.sloth.bizs.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.s1995.sloth.bizs.constant.BizCst;
import cn.s1995.sloth.bizs.constant.CacheNames;
import cn.s1995.sloth.common.core.constant.enums.hint.Hint;
import cn.s1995.sloth.bizs.entity.Order;
import cn.s1995.sloth.bizs.entity.OrderItem;
import cn.s1995.sloth.bizs.entity.OrderRefund;
import cn.s1995.sloth.bizs.entity.Sku;
import cn.s1995.sloth.bizs.event.CancelOrderEvent;
import cn.s1995.sloth.bizs.event.ReceiptOrderEvent;
import cn.s1995.sloth.bizs.event.SubmitOrderEvent;
import cn.s1995.sloth.bizs.mapper.OrderMapper;
import cn.s1995.sloth.bizs.mapper.ProdMapper;
import cn.s1995.sloth.bizs.mapper.SkuMapper;
import cn.s1995.sloth.bizs.oo.req.OrderListREQ;
import cn.s1995.sloth.bizs.oo.vo.OrderItemVO;
import cn.s1995.sloth.bizs.oo.vo.OrderVO;
import cn.s1995.sloth.bizs.oo.vo.ShopCartOrderMergerVO;
import cn.s1995.sloth.bizs.service.OrderItemService;
import cn.s1995.sloth.bizs.service.OrderService;
import cn.s1995.sloth.common.core.util.R;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.yulichang.base.MPJBaseServiceImpl;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import lombok.AllArgsConstructor;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 订单
 *
 * @author sloth
 * @date 2023-12-15 15:26:19
 */
@Service
@AllArgsConstructor
public class OrderServiceImpl extends MPJBaseServiceImpl<OrderMapper, Order> implements OrderService {

	private final OrderItemService orderItemService;
	private final ApplicationEventPublisher eventPublisher;
	private final ProdMapper prodMapper;
	private final SkuMapper skuMapper;

	@Override
	@CachePut(cacheNames = CacheNames.confirmOrder, key = "#userId")
	public ShopCartOrderMergerVO putConfirmOrderCache(Long userId, ShopCartOrderMergerVO shopCartOrderMergerDto) {
		return shopCartOrderMergerDto;
	}

	@Override
	@Cacheable(cacheNames = CacheNames.confirmOrder, key = "#userId")
	public ShopCartOrderMergerVO getConfirmOrderCache(Long userId) {
		return null;
	}

	@Override
	@CacheEvict(cacheNames = CacheNames.confirmOrder, key = "#userId")
	public void removeConfirmOrderCache(Long userId) {
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public List<Order> submit(Long userId, ShopCartOrderMergerVO mergerOrder) {
		List<Order> orderList = new ArrayList<>();
		// 通过事务提交订单
		eventPublisher.publishEvent(new SubmitOrderEvent(mergerOrder, orderList));

		// 插入订单
		saveBatch(orderList);
		List<OrderItem> orderItems = orderList.stream().flatMap(order -> order.getOrderItems().stream()).collect(Collectors.toList());
		// 插入订单项，返回主键
		orderItemService.saveBatch(orderItems);
		return orderList;
	}

	@Override
	public Page pageWithOrderItem(Page page, MPJLambdaWrapper<Order> wrapper) {
		//查询订单
		Page<OrderVO> resultPage = baseMapper.selectJoinPage(page, OrderVO.class, wrapper);
		this.setOrderItem(resultPage.getRecords());
		return resultPage;
	}

	@Override
	public List<OrderVO> setOrderItem(List<OrderVO> list) {
		if(ObjectUtil.isNotEmpty(list)) {
			//查询订单项
			List<Long> ids = list.stream().map(o -> o.getId()).collect(Collectors.toList());
			List<OrderItemVO> items = orderItemService.selectJoinList(
					OrderItemVO.class,
					new MPJLambdaWrapper<OrderItem>()
						.selectAll(OrderItem.class)
						.selectAssociation(OrderRefund.class, OrderItemVO::getOrderRefund)
						.selectAs(Sku::getProperties, OrderItemVO::getProperties)
						.leftJoin(OrderRefund.class, on -> on
								.eq(OrderRefund::getOrderItemId, OrderItem::getId)
								.ne(OrderRefund::getState, BizCst.OrderRefund.State.cancel))
						.leftJoin(Sku.class, Sku::getId, OrderItem::getSkuId)
						.in(OrderItem::getOrderId, ids)
			);
			//分组
			Map<Long, List<OrderItemVO>> itemGroup = items.stream()
					.map(e -> {
						if(ObjectUtil.isNotNull(e.getFreightAmount())){
							e.setGrandTotal(e.getTotalAmount().add(e.getFreightAmount()));
						}else {
							e.setGrandTotal(e.getTotalAmount());
						}
						return e;
					})
					.collect(Collectors.groupingBy(OrderItemVO::getOrderId));
			//绑定order
			for (OrderVO order : list) {
				order.setOrderItemList(itemGroup.get(order.getId()));
				if(ObjectUtil.isNotEmpty(order.getOrderItemList())) {
					for (OrderItemVO itemVO : order.getOrderItemList()) {
						if(ObjectUtil.isNotNull(itemVO.getOrderRefund())) {
							order.setHasRefund(true);
							break;
						}
					}
				}
			}
		}
		return list;
	}

	@Override
	public R updateReduceAmount(Long id, BigDecimal reduceAmount) {
		Order order = this.getById(id);
		if(ObjectUtil.isNull(order))
			return R.failed(Hint.NOT_FOUND);
		if(BizCst.Order.State.unpay != order.getState())
			return R.failed(Hint.FORBIDDEN);
		if(reduceAmount.compareTo(new BigDecimal(0)) < 0 || reduceAmount.compareTo(order.getOrderAmount()) > 0)
			return R.failed(Hint.BAD_PARAMETER);
		order.setReduceAmount(reduceAmount);
		this.updateById(order);
		return R.ok();

	}

	@Override
	public R closeOrder(Long id) {
		Order order = this.getById(id);
		if(ObjectUtil.isNull(order))
			return R.failed(Hint.NOT_FOUND);
		if(BizCst.Order.State.unpay != order.getState())
			return R.failed(Hint.FORBIDDEN);
		order.setState(BizCst.Order.State.canceled);
		this.updateById(order);
		return R.ok();
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public void cancelOrders(List<Order> orders, Boolean... isAutoCancel) {
		if (CollectionUtil.isEmpty(orders)) {
			return;
		}
		LambdaUpdateWrapper<Order> wrapper = Wrappers.lambdaUpdate(Order.class)
				.set(Order::getState, BizCst.Order.State.canceled)
				.set(Order::getCancelTime, LocalDateTime.now())
				.eq(Order::getIsPayed, Boolean.FALSE)
				.in(Order::getId, orders.stream().map(Order::getId).collect(Collectors.toList()));
		// 如果是自动取消，设置自动取消标识
		if (ArrayUtil.isNotEmpty(isAutoCancel)) {
			wrapper.set(Order::getIsAutoCancel, isAutoCancel[0]);
		}
		update(wrapper);

		List<OrderItem> allOrderItems = new ArrayList<>();
		for (Order order : orders) {
			List<OrderItem> orderItems = order.getOrderItems();
			allOrderItems.addAll(orderItems);
			eventPublisher.publishEvent(new CancelOrderEvent(order));
		}
		if (CollectionUtil.isEmpty(allOrderItems)) {
			return;
		}
		Map<Long, Integer> prodCollect = new HashMap<>(16);
		Map<Long, Integer> skuCollect = new HashMap<>(16);

		allOrderItems.stream().collect(Collectors.groupingBy(OrderItem::getProdId)).forEach((prodId, orderItems) -> {
			int prodTotalNum = orderItems.stream().mapToInt(OrderItem::getNum).sum();
			prodCollect.put(prodId, prodTotalNum);
		});
		prodMapper.returnStock(prodCollect);

		allOrderItems.stream().collect(Collectors.groupingBy(OrderItem::getSkuId)).forEach((skuId, orderItems) -> {
			int prodTotalNum = orderItems.stream().mapToInt(OrderItem::getNum).sum();
			skuCollect.put(skuId, prodTotalNum);
		});
		skuMapper.returnStock(skuCollect);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public void confirmReceiptOrder(List<Order> orders) {
		update(
				Wrappers.lambdaUpdate(Order.class)
						.set(Order::getState, BizCst.Order.State.completed)
						.set(Order::getCompleteTime, LocalDateTime.now())
						.in(Order::getId, orders.stream().map(Order::getId).collect(Collectors.toList()))
		);
		for (Order order : orders) {
			eventPublisher.publishEvent(new ReceiptOrderEvent(order));
		}
	}

	@Override
	public List<Order> getOrderAndOrderItems(Integer state, LocalDateTime lessThanUpdateTime) {
		MPJLambdaWrapper<Order> wrapper = new MPJLambdaWrapper<Order>()
				.selectCollection(OrderItem.class, Order::getOrderItems)
				.leftJoin(OrderItem.class, OrderItem::getOrderId, Order::getId)
				.eq(Objects.nonNull(state), Order::getState, state)
				.lt(Objects.nonNull(lessThanUpdateTime), Order::getUpdateTime, lessThanUpdateTime);

		return this.selectJoinList(Order.class, wrapper);

		// return baseMapper.getOrderAndOrderItems(state, lessThanUpdateTime);
	}

	@Override
	public List<Order> getOrderAndItemsList(List<Long> ids) {
		if (CollUtil.isEmpty(ids)) {
			return new ArrayList<>();
		}

		MPJLambdaWrapper<Order> wrapper = new MPJLambdaWrapper<Order>()
				.selectCollection(OrderItem.class, Order::getOrderItems)
				.leftJoin(OrderItem.class, OrderItem::getOrderId, Order::getId)
				.in(Order::getId, ids);

		return this.selectJoinList(Order.class, wrapper);
	}

	@Override
	public List<Long> getOrderIdsByPayFlowId(Long payFlowId) {
		return list(
				Wrappers.lambdaQuery(Order.class)
						.select(Order::getId)
						.eq(Order::getPayFlowId, payFlowId)
		).stream().map(Order::getId).toList();
	}

	@Override
	public List<Order> getOrderListByPayFlowId(Long payFlowId) {
		return list(
				Wrappers.lambdaQuery(Order.class)
						.eq(Order::getPayFlowId, payFlowId)
		);
	}

	@Override
	public void updateOrderPayFlowId(List<Order> orderList, String payFlowId, String transactionId, Integer payMethod) {
		if (CollUtil.isEmpty(orderList)) {
			return;
		}
		List<Long> orderIds = orderList.stream().map(Order::getId).collect(Collectors.toList());
		update(
				Wrappers.lambdaUpdate(Order.class)
						.set(Order::getPayFlowId, payFlowId)
						.set(Order::getTransactionId, transactionId)
						.set(Order::getPayMethod, payMethod)
						.in(Order::getId, orderIds)
		);
	}

	@Override
	public Page getHasRefundOrderPage(Page page, OrderListREQ req) {
		Page<OrderVO> resultPage = baseMapper.getHasRefundOrderPage(page, req);
		this.setOrderItem(resultPage.getRecords());
		return resultPage;
	}

}
